#pragma once

/************************************************************************/
/*                                                                      */
/************************************************************************/

BEGIN_NAMESPACE_OXGE
template <typename T> 
class MATHLIB_TRANSFER Vector4T
{
public:
	typedef T ValueType;
	typedef Vector3T<T> Vector3Type;
	typedef Vector4T<T> VectorType;
	

	Vector4T(){  }
	Vector4T(T x, T y, T z, T p){ Set(x,y,z,p); }
	Vector4T(const T* cv){ Set(cv); }
	Vector4T(const Vector3Type& vec3 ){	Set(vec3.x, vec3.y, vec3.z , 0); }

	inline VectorType& Set(T ax, T ay,T az, T ap){ x = ax; y = ay; z = az; p = ap; return *this; }
	inline VectorType& Set(const T* cv){ memcpy(c_v, cv, sizeof(T)*NLen); return *this; }

	VectorType& operator *= (const T& dScal ){ x*=dScal; y*=dScal; z*=dScal; p*=dScal; return *this;}	
	VectorType& operator += (const VectorType& vR ){ x+=vR.x; y+=vR.y; z+=vR.z; p+=vR.p; return *this; }
	VectorType& operator -= (const VectorType& vR ){ x-=vR.x; y-=vR.y; z-=vR.z; p-=vR.p; return *this; }

	VectorType operator + (const VectorType& vR)const { return VectorType(x+vR.x,y+vR.y,z+vR.z, p+vR.p); }
	VectorType operator - (const VectorType& vR)const { return VectorType(x-vR.x,y-vR.y,z-vR.z,p-vR.p); }
	VectorType operator * (const T& rVal )const { return VectorType(x*rVal,y*rVal,z*rVal,p*rVal); } 
	friend VectorType operator *(const T& lVal, const VectorType& rVec){	return rVec * lVal; }


	//
	//ValueType& operator[](size_t idx){ 	assert(idx < NLen);	return c_v[idx]; }
	//
	//const ValueType& operator[](size_t idx)const{ assert(idx < NLen);	return c_v[idx]; }
	
	inline operator ValueType*() { return c_v; }	
	inline operator const ValueType*()const { return c_v; }

	//dot
	ValueType dot(const VectorType& vectR)const	{ return x * vectR.x + y * vectR.y + z * vectR.z + p * vectR.p;	}

	//cross
	VectorType cross(const VectorType& vec1, const VectorType&vec2)const;

	//normalize
	inline VectorType& Normalize(){	return (*this)*=(ValueType(1)/Length()); }

	//square of len
	inline ValueType SquareLength()const{ return x*x + y*y + z*z + p*p; }
	//length
	inline ValueType Length()const{	return sqrt(SquareLength());}
	//
	inline VectorType& SetLength(const T& len){	return (*this)*=(len/Length());	}
	//
	inline VectorType GetNormVector()const{ VectorType vec = *this;	return vec.Normalize();	}

public:
	const static size_t NLen = 4;	
	union{
		struct{
			T x,y,z,p;
		};
		T c_v[NLen];
	};

	static const VectorType ZERO;
	static const VectorType UNIT_X;
	static const VectorType UNIT_Y;
	static const VectorType UNIT_Z;
	static const VectorType UNIT_P;
	static const VectorType UNIT_SCALE;

public:
	//testing all functions
	static void UnitTest();

};



//Global io
template<typename T>
std::ostream& operator<<(std::ostream& os, const Vector4T<T>& vec);


template<typename T>
std::istream& operator>>(std::istream& is, Vector4T<T>& vec);


END_NAMESPACE_OXGE